package com.checknull.druineage.element;

import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.alibaba.druid.util.StringUtils;
import com.checknull.druineage.customizable.UsedForCal;

import java.util.*;

public class Table {

    /**
     * 所属schema
     */
    private Schema owner;

    /**
     * 表名
     */
    private String name;

    /**
     * 字段
     */
    private List<Column> columns = new ArrayList<>();

    /**
     * 是否是临时表
     *
     * @description 解析过程中用于临时存放字段的集合
     */
    private boolean isTemp = false;

    /**
     * 是否是删除表
     *
     * @description 来源于删表语句
     */
    private boolean isDrop = false;

    /**
     * 表（或子查询）在sql中每一处的别名
     * key：表或子查询所在的域（最小单元的的整个select部分）
     * value：别名
     */
    private Map<SQLSelectQueryBlock, Set<String>> alias = new HashMap<>();

    public Schema getOwner() {
        return owner;
    }

    public void setOwner(Schema owner) {
        this.owner = owner;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Column> getColumns() {
        return columns;
    }

    public boolean isTemp() {
        return isTemp;
    }

    public void isTemp(boolean temp) {
        isTemp = temp;
    }

    public boolean isDrop() {
        return isDrop;
    }

    public void isDrop(boolean drop) {
        isDrop = drop;
    }

    public Set<String> getAlias(SQLSelectQueryBlock key) {
        return alias.get(key);
    }

    public void putAlias(SQLSelectQueryBlock key, String alias) {
        if (!StringUtils.isEmpty(alias)) {
            if (!this.alias.containsKey(key)) {
                this.alias.put(key, new HashSet<>());
            }
            getAlias(key).add(alias);
        }
    }

    /**
     * 合并并返回字段
     *
     * @param column 字段
     * @return 正确的字段
     * @description 将传入的字段与现有的字段集合进行比较，若已存在相同的，则使用现有的字段
     * 若不存在，则添加进字段集合，整理完之后再返回正确的字段
     */
    public Column absorbColumn(Column column) {
        if (StringUtils.isEmpty(column.getName()) && !column.isVal()) {
            return null;
        }
        if (column.isVal()) {
            for (Column col : columns) {
                if (col.isVal()
                        && ((column.getName() == null && col.getName() == null)
                        || (column.getName() != null && col.getName() != null
                        && column.getName().equalsIgnoreCase(col.getName())))) {
                    return col;
                }
            }
        } else {
            for (Column col : columns) {
                if (column.getName().equalsIgnoreCase(col.getName())) {
                    return col.absorb(column);
                }
            }
        }
        columns.add(column);
        column.setOwner(column.isVal() ? null : this);  //常量没有所属表
        return column;
    }

    /**
     * 合并并返回字段（重新排序）
     *
     * @param column      字段
     * @param reSortIndex 重新排序索引
     * @return 正确的字段
     * @description 同一张表可以有多条血缘，每种血缘解析的时候字段顺序不一样，所以重新排序才能确保解析正确
     */
    public Column absorbColumn(Column column, int reSortIndex) {
        Column col = absorbColumn(column);
        columns.remove(col);
        columns.add(reSortIndex, col);
        return col;
    }

    /**
     * 表吸收
     *
     * @param table 被吸收表
     * @return 吸收完成后的表
     * @description 吸收并不覆盖，优先保留自有的。吸收内容：传入表的所有字段
     */
    public Table absorb(Table table) {
        for (Column column : table.getColumns()) {
            Column thisColumn = absorbColumn(column);
            if (!column.equals(thisColumn) && !thisColumn.isVal()) {
                thisColumn.absorb(column);
            }
        }
        return this;
    }

    /**
     * 清除表中的所有常量字段
     */
    public void clearValColumn() {
        columns.removeIf(Column::isVal);
    }

    /**
     * 清理临时表
     *
     * @param usedForCal 血缘作用计算器
     */
    public void clearTempTable(UsedForCal usedForCal) {
        for (Column column : columns) {
            column.clearTempTable(usedForCal);
        }
    }

    /**
     * 清理删除表
     *
     * @param usedForCal 血缘作用计算器
     */
    public void clearDropTable(UsedForCal usedForCal) {
        for (Column column : columns) {
            column.clearDropTable(usedForCal);
        }
    }

    /**
     * 清理中间表
     *
     * @param usedForCal 血缘作用计算器
     */
    public void clearMidTable(UsedForCal usedForCal) {
        for (Column column : columns) {
            column.clearMidTable(usedForCal);
        }
    }

    @Override
    public String toString() {
        return StringUtils.isEmpty(name) ? super.toString() : name;
    }

}
